package org.rency.common.trigger.factory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.rency.common.trigger.schedule.DaemonContext;

/**
 * 数据集合任务处理模板
 * @author: user_rcy@163.com
 * </br>Created on 2016年5月5日
 * @version: V1.0.0
 * @since JDK 1.8 
 * @param <T>
 * @param <Result>
 */
public abstract class DataDaemonTemplate<T, Result> extends AbstractDaemonJob {

	@Override
	public void executeJob(DaemonContext context) {
		AtomicBoolean hasMore = new AtomicBoolean(true);
		AtomicInteger page = new AtomicInteger(1);
		while(hasMore.get()){
			//加载数据队列
            List<TaskData<T>> dataList = loadAndCompose(page);
            if (CollectionUtils.isEmpty(dataList)) {
                logger.info("[{}]没有数据需要执行", this.getTaskName());
                break;
            } else if (dataList.size() < this.getBatchSize()) {
                hasMore.set(false);
            }
            page.incrementAndGet();
            
            //定义结果集
            Map<String, FutureTask<Result>> futureMap = new HashMap<String,FutureTask<Result>>();
            Iterator<TaskData<T>> iterator = dataList.iterator();
            while(iterator.hasNext()){
            	TaskData<T> data = iterator.next();
            	FutureTask<Result> futureTask = new FutureTask<Result>(new DefaultCallable(data));
            	futureMap.put(data.getKey(), futureTask);
            	logger.debug("提交任务[{}]=[{}]至队列.",this.getTaskName(),data.getKey());
            	this.getDaemonTaskExecutor().execute(futureTask);
            }
            
            //从线程池中获得执行结果
            for(Entry<String, FutureTask<Result>> entry : futureMap.entrySet()){
            	try {
					Result result = entry.getValue().get();
					logger.info("任务[{}]执行结果 : [{}]->[{}].",this.getTaskName(),entry.getKey(),result);
				} catch (InterruptedException e) {
					logger.error("任务[{}]执行结果获取异常中断 : [{}].",this.getTaskName(),entry.getKey(),e);
				} catch (ExecutionException e) {
					logger.error("任务[{}]执行结果获取异常 : [{}].",this.getTaskName(),entry.getKey(),e);
				}
            }
		}		
	}
	
	private List<TaskData<T>> loadAndCompose(AtomicInteger startIndex) {
        List<TaskData<T>> dataList = new ArrayList<TaskData<T>>();
        List<T> datas = loadData(startIndex);
        logger.info("[{}]任务加载数据完成，条数:[{}]", this.getTaskName(), datas.size());
        if(CollectionUtils.isEmpty(datas)) {
            return new ArrayList<TaskData<T>>();
        }
        for (T t : datas) {
            dataList.add(new TaskData<T>(getKey(t), t));
        }
        return dataList;
    }
	
	/**
	 * 任务调用
	 * @author: user_rcy@163.com
	 * </br>Created on 2016年5月5日
	 * @version: V1.0.0
	 * @since JDK 1.8
	 */
	protected class DefaultCallable implements Callable<Result> {
        /** 任务数据对象 */
        private final TaskData<T> taskData;

        /**
         * 构造
         * @param taskData
         */
        public DefaultCallable(TaskData<T> taskData) {
            this.taskData = taskData;
        }

        public Result call() throws Exception {
        	//业务逻辑实现
            return process(taskData.getData());
        }

    }
	
	/**
	 * 任务数据对象(内部)
	 * @author: user_rcy@163.com
	 * </br>Created on 2016年5月5日
	 * @version: V1.0.0
	 * @since JDK 1.8 
	 * @param <D>
	 */
	protected class TaskData<D> {
        private String key;
        private D      data;

        public TaskData(String key, D data) {
            this.key = key;
            this.data = data;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public D getData() {
            return data;
        }

        public void setData(D data) {
            this.data = data;
        }

        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE);
        }
    }

	public abstract List<T> loadData(AtomicInteger pageNo);
	
	public abstract String getKey(T domain);
	
	public abstract Result process(T domain);
}